package org.keyyond.ms.scheduler.config;

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.util.Properties;

import org.quartz.Scheduler;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

import com.mchange.v2.c3p0.ComboPooledDataSource;

@Configuration
public class QuartzConfiger {
	@Value("${quartz.scheduler.instanceName}")
	private String quartzInstanceName;

	@Value("${org.quartz.dataSource.myDS.driver}")
	private String myDSDriver;

	@Value("${org.quartz.dataSource.myDS.URL}")
	private String myDSURL;

	@Value("${org.quartz.dataSource.myDS.user}")
	private String myDSUser;

	@Value("${org.quartz.dataSource.myDS.password}")
	private String myDSPassword;

	@Value("${org.quartz.dataSource.myDS.maxConnections}")
	private String myDSMaxConnections;
	
	@Autowired
	private MyJobFactory myJobFactory;

	/**
	 * 设置属性
	 * 
	 * @return
	 * @throws IOException
	 */
	private Properties quartzProperties() throws IOException {
		Properties prop = new Properties();
		// 调度标识名 集群中每一个实例都必须使用相同的名称
		prop.put("quartz.scheduler.instanceName", quartzInstanceName);

		// ID设置为自动获取 每一个必须不同
		prop.put("org.quartz.scheduler.instanceId", "AUTO");
		prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
		prop.put("org.quartz.scheduler.jmx.export", "true");

		// 数据保存方式为持久化.将任务持久化到数据中。因为集群中节点依赖于数据库来传播Scheduler实例的状态，你只能在使用JDBC
		// JobStore时应用Quartz集群。
		prop.put("org.quartz.jobStore.class",
				"org.quartz.impl.jdbcjobstore.JobStoreTX");
		prop.put("org.quartz.jobStore.driverDelegateClass",
				"org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
		prop.put("org.quartz.jobStore.dataSource", "quartzDataSource");
		prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
		// Cluster开启集群.通知Scheduler实例要它参与到一个集群当中。
		prop.put("org.quartz.jobStore.isClustered", "true");

		// 属性定义了Scheduler实例检入到数据库中的频率(单位：毫秒)。
		// Scheduler检查是否其他的实例到了它们应当检入的时候未检入；
		// 这能指出一个失败的Scheduler实例，且当前 Scheduler会以此来接管任何执行失败并可恢复的Job。
		// 通过检入操作，Scheduler 也会更新自身的状态记录。clusterChedkinInterval越小，
		// Scheduler节点检查失败的Scheduler实例就越频繁。默认值是 15000 (即15 秒)。
		prop.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
		// 库的别名
		prop.put("org.quartz.jobStore.dataSource", "myDS");
		prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
		prop.put("org.quartz.jobStore.misfireThreshold", "120000");
		prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
		prop.put("org.quartz.jobStore.selectWithLockSQL",
				"SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE");

		prop.put("org.quartz.threadPool.class",
				"org.quartz.simpl.SimpleThreadPool");
		// 线程数量
		prop.put("org.quartz.threadPool.threadCount", "10");
		// 线程优先级
		prop.put("org.quartz.threadPool.threadPriority", "5");
		prop.put(
				"org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread",
				"true");

		prop.put("org.quartz.plugin.triggHistory.class",
				"org.quartz.plugins.history.LoggingJobHistoryPlugin");
		prop.put("org.quartz.plugin.shutdownhook.class",
				"org.quartz.plugins.management.ShutdownHookPlugin");
		prop.put("org.quartz.plugin.shutdownhook.cleanShutdown", "true");
		return prop;
	}

	/**
	 * 调度工厂
	 * 
	 * @param cronJobTrigger1
	 * @return
	 * @throws IOException
	 * @throws PropertyVetoException
	 */
	@Bean
	public SchedulerFactoryBean schedulerFactoryBean()
			throws IOException, PropertyVetoException {
		SchedulerFactoryBean factory = new SchedulerFactoryBean();
		// this allows to update triggers in DB when updating settings in config
		// file:
		// 用于quartz集群,QuartzScheduler
		// 启动时更新己存在的Job，这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
		factory.setOverwriteExistingJobs(true);
		// 用于quartz集群,加载quartz数据源
		// factory.setDataSource(dataSource);
		// QuartzScheduler 延时启动，应用启动完10秒后 QuartzScheduler 再启动
		factory.setStartupDelay(10);
		// 用于quartz集群,加载quartz数据源配置
		factory.setQuartzProperties(quartzProperties());
		// 设置自动启动
		factory.setAutoStartup(true);
		factory.setApplicationContextSchedulerContextKey("applicationContext");
		factory.setDataSource(createDataSource());
		// 注册触发器
//		factory.setTriggers(cronJobTrigger1, cronJobTrigger2);// 直接使用配置文件
		// factory.setConfigLocation(new
		// FileSystemResource(this.getClass().getResource("/quartz.properties").getPath()));
		factory.setSchedulerName("SpringBootSchedulerDemo");
		factory.setJobFactory(myJobFactory);
		return factory;
	}
	
	/*
     * quartz初始化监听器
     */
    @Bean
    public QuartzInitializerListener executorListener() {
       return new QuartzInitializerListener();
    }

	 /*
     * 通过SchedulerFactoryBean获取Scheduler的实例
     */
    @Bean(name="Scheduler")
    public Scheduler scheduler() throws IOException, PropertyVetoException {
        return schedulerFactoryBean().getScheduler();
    }
    
	/**
	 * 加载job
	 * 
	 * @return
	 */
//	@Bean()
//	public JobDetailFactoryBean updateDialogStatusJobDetail() {
//		return createJobDetail(InvokingJobDetailDetailFactory.class,
//				"updateDialogStatusGroup", "dialogJob");
//	}

	/**
	 * 加载job
	 * 
	 * @return
	 */
//	@Bean()
//	public JobDetailFactoryBean testJobDetail() {
//		return createJobDetail(InvokingJobDetailDetailFactory.class,
//				"testJobDetailGroup", "testJob");
//	}

	/**
	 * 加载触发器
	 * 
	 * @param jobDetail
	 * @return
	 */
//	@Bean(name = "dialogJobTrigger")
//	public CronTriggerFactoryBean dialogStatusJobTrigger(
//			@Qualifier("updateDialogStatusJobDetail") JobDetail jobDetail) {
//		return dialogStatusTrigger(jobDetail, "*/10 * * * * ?");
//	}

	/**
	 * 加载触发器
	 * 
	 * @param jobDetail
	 * @return
	 */
//	@Bean(name = "testJobTrigger")
//	public CronTriggerFactoryBean testJobTrigger(
//			@Qualifier("testJobDetail") JobDetail jobDetail) {
//		return dialogStatusTrigger(jobDetail, "*/3 * * * * ?");
//	}

	/**
	 * 数据库链接池
	 * 
	 * @return
	 * @throws PropertyVetoException
	 */
	@Bean
	public ComboPooledDataSource createDataSource()
			throws PropertyVetoException {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass(myDSDriver);
		dataSource.setJdbcUrl(myDSURL);
		dataSource.setUser(myDSUser);
		dataSource.setPassword(myDSPassword);
		return dataSource;
	}

	/**
	 * 创建job工厂
	 * 
	 * @param jobClass
	 * @param groupName
	 * @param targetObject
	 * @return
	 */
//	private static JobDetailFactoryBean createJobDetail(Class<?> jobClass, String groupName, String targetObject) {
//        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
//        factoryBean.setJobClass(jobClass);
//        factoryBean.setDurability(true);
//        factoryBean.setRequestsRecovery(true);
//        factoryBean.setGroup(groupName);
//        Map<String, String> map = new HashMap();
//        map.put("targetObject", targetObject);
//        map.put("targetMethod", "execute");
//        factoryBean.setJobDataAsMap(map);
//        return factoryBean;
//    }

	/**
	 * 创建触发器工厂
	 * 
	 * @param jobDetail
	 * @param cronExpression
	 * @return
	 */
//	private static CronTriggerFactoryBean dialogStatusTrigger(
//			JobDetail jobDetail, String cronExpression) {
//		CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
//		factoryBean.setJobDetail(jobDetail);
//		factoryBean.setCronExpression(cronExpression);
//		return factoryBean;
//	}
	
	@Component
	public class MyJobFactory extends AdaptableJobFactory {
	    
	    @Autowired
	    private AutowireCapableBeanFactory capableBeanFactory;

	    @Override
	    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
	        // 调用父类的方法
	        Object jobInstance = super.createJobInstance(bundle);
	        // 进行注入
	        capableBeanFactory.autowireBean(jobInstance);
	        return jobInstance;
	    }
	}
}
